package gorm

import (
	"encoding/xml"
	"gitee.com/molonglove/goboot/logger"
	"gitee.com/molonglove/goboot/pongo"
	"io/fs"
)

type (

	/**  ================= xml文件对应解析参数 =========================== */

	// XmlRootDom 对应一个实体的查询模型
	XmlRootDom struct {
		Name      xml.Name    `xml:"mapper"`
		Namespace string      `xml:"namespace,attr"`
		Selects   []SelectDom `xml:"select"`
		Updates   []UpdateDom `xml:"update"`
		Inserts   []InsertDom `xml:"insert"`
		Deletes   []DeleteDom `xml:"delete"`
	}

	// SelectDom 查询
	SelectDom struct {
		Name   xml.Name `xml:"select"`
		Id     string   `xml:"id,attr"`
		Param  string   `xml:"param,attr"`
		Result string   `xml:"result,attr"`
		Value  string   `xml:",innerxml"`
	}

	// UpdateDom 更新
	UpdateDom struct {
		Name   xml.Name `xml:"update"`
		Id     string   `xml:"id,attr"`
		Param  string   `xml:"param,attr"`
		Query  string   `xml:"query,attr"`
		Result string   `xml:"result,attr"`
		Value  string   `xml:",innerxml"`
	}

	// DeleteDom 删除
	DeleteDom struct {
		Name   xml.Name `xml:"delete"`
		Id     string   `xml:"id,attr"`
		Param  string   `xml:"param,attr"`
		Result string   `xml:"result,attr"`
		Value  string   `xml:",innerxml"`
	}

	// InsertDom 插入
	InsertDom struct {
		Name   xml.Name `xml:"insert"`
		Id     string   `xml:"id,attr"`
		Param  string   `xml:"param,attr"`
		Result string   `xml:"result,attr"`
		Value  string   `xml:",innerxml"`
	}

	/**  ================= 动态模型 =========================== */

	// DynamicType 动态模型类型
	DynamicType int

	DynamicItem = map[string]*DynamicNode

	// DynamicModel mapper模型
	DynamicModel struct {
		TableName string // 表名
		Nodes     map[DynamicType]DynamicItem
	}

	// DynamicNode SQL模型
	DynamicNode struct {
		Id         string          // SQL名称
		Content    string          // SQL内容
		ParamName  string          // 参数类型名称
		QueryName  string          // 查询参数
		ResultName string          // 返回类型名称
		Param      any             // 参数内容
		Query      any             // 查询参数
		Tpl        *pongo.Template // 模板
		Offset     int
		Limit      int
	}
)

const (
	Select DynamicType = iota
	Update
	Delete
	Insert
)

func (m *XmlRootDom) to(log logger.ILogger) (*DynamicModel, error) {
	var (
		tpl      *pongo.Template
		itemNode = make(DynamicItem)
		nodes    = make(map[DynamicType]DynamicItem)
		err      error
	)
	if len(m.Selects) > 0 {
		for _, dom := range m.Selects {
			if tpl, err = pongo.FromString(dom.Value); err != nil {
				log.Error("解析Select的动态模板[%s]内容失败：%s", dom.Id, err.Error())
				continue
			}
			paramName := dom.Param
			if len(paramName) <= 0 {
				paramName = "param"
			}
			itemNode[dom.Id] = &DynamicNode{
				Id:         dom.Id,
				Content:    dom.Value,
				ParamName:  paramName,
				ResultName: dom.Result,
				Tpl:        tpl,
			}
		}
		nodes[Select] = itemNode
	}

	if len(m.Updates) > 0 {
		itemNode = make(DynamicItem)
		for _, dom := range m.Updates {
			if tpl, err = pongo.FromString(dom.Value); err != nil {
				log.Error("解析Update的动态模板[%s]内容失败：%s", dom.Id, err.Error())
				continue
			}
			paramName := dom.Param
			if len(paramName) <= 0 {
				paramName = "param"
			}
			queryName := dom.Query
			if len(queryName) <= 0 {
				queryName = "query"
			}
			itemNode[dom.Id] = &DynamicNode{
				Id:         dom.Id,
				Content:    dom.Value,
				ParamName:  paramName,
				QueryName:  queryName,
				ResultName: dom.Result,
				Tpl:        tpl,
			}
		}
		nodes[Update] = itemNode
	}

	if len(m.Inserts) > 0 {
		itemNode = make(DynamicItem)
		for _, dom := range m.Inserts {
			if tpl, err = pongo.FromString(dom.Value); err != nil {
				log.Error("解析Update的动态模板[%s]内容失败：%s", dom.Id, err.Error())
				continue
			}
			itemNode[dom.Id] = &DynamicNode{
				Id:         dom.Id,
				Content:    dom.Value,
				ParamName:  dom.Param,
				ResultName: dom.Result,
				Tpl:        tpl,
			}
		}
		nodes[Insert] = itemNode
	}

	if len(m.Deletes) > 0 {
		itemNode = make(DynamicItem)
		for _, dom := range m.Deletes {
			if tpl, err = pongo.FromString(dom.Value); err != nil {
				log.Error("解析Update的动态模板[%s]内容失败：%s", dom.Id, err.Error())
				continue
			}
			paramName := dom.Param
			if len(paramName) <= 0 {
				paramName = "param"
			}
			itemNode[dom.Id] = &DynamicNode{
				Id:         dom.Id,
				Content:    dom.Value,
				ParamName:  paramName,
				ResultName: dom.Result,
				Tpl:        tpl,
			}
		}
		nodes[Delete] = itemNode
	}
	log.Info("动态模板[%s]解析完成", m.Namespace)
	return &DynamicModel{
		TableName: m.Namespace,
		Nodes:     nodes,
	}, nil
}

// Build 解析动态模板
func (d *DynamicNode) Build() (string, []any, error) {
	var (
		result string
		param  = pongo.Context{}
		err    error
	)
	// 解析
	if len(d.ParamName) > 0 && d.Param != nil {
		param[d.ParamName] = d.Param
	}
	if len(d.QueryName) > 0 && d.Query != nil {
		param[d.QueryName] = d.Query
	}
	if result, err = d.Tpl.Execute(param); err != nil {
		return "", nil, err
	}
	// 返回解析后的SQL和需要预编译的值
	return result, d.Tpl.Vars, nil
}

func DynamicParser(file fs.File, log logger.ILogger) (*DynamicModel, error) {
	var (
		mapper XmlRootDom
		err    error
	)
	decoder := xml.NewDecoder(file)
	if err = decoder.Decode(&mapper); err != nil {
		log.Error("读取文件内容失败：%s", err.Error())
		return nil, err
	}
	return mapper.to(log)
}
